package com.smmisia.shop.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smmisia.shop.entity.ProductInventoryStream;
import com.smmisia.shop.mapper.ProductMapper;
import com.smmisia.shop.request.InventoryRequest;
import com.smmisia.shop.response.ProductInventoryResponse;
import com.smmisia.shop.service.ProductInventoryStreamService;
import com.smmisia.shop.mapper.ProductInventoryStreamMapper;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;


import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.smmisia.common.response.ResponseCode.BIZ_ERROR;
import static com.smmisia.common.response.ResponseCode.DUPLICATED;


@Service
@Slf4j
public class ProductInventoryStreamServiceImpl extends ServiceImpl<ProductInventoryStreamMapper, ProductInventoryStream>
        implements ProductInventoryStreamService, InitializingBean {

    private static final String PRODUCT_KEY = "pd:inventory:";

    private static final String INVENTORY_STREAM_KEY = "pd:inventory:stream:";

    private static final String ERROR_CODE_INVENTORY_NOT_ENOUGH = "INVENTORY_NOT_ENOUGH";

    private static final String ERROR_CODE_KEY_NOT_FOUND = "KEY_NOT_FOUND";

    private static final String ERROR_CODE_OPERATION_ALREADY_EXECUTED = "OPERATION_ALREADY_EXECUTED";

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public ProductInventoryResponse init(InventoryRequest request) {
        ProductInventoryResponse productInventoryResponse = new ProductInventoryResponse();
        if (redissonClient.getBucket(getCacheKey(request)).isExists()) {
            productInventoryResponse.setSuccess(true);
            productInventoryResponse.setResponseCode(DUPLICATED.name());
            return productInventoryResponse;
        }
        redissonClient.getBucket(getCacheKey(request)).set(request.getInventory());
        productInventoryResponse.setSuccess(true);
        productInventoryResponse.setProductId(Long.valueOf(request.getProductId()));
        productInventoryResponse.setIdentifier(request.getIdentifier());
        productInventoryResponse.setInventory(request.getInventory());
        return productInventoryResponse;

    }

    @Override
    public Integer getInventory(InventoryRequest request) {
        String cacheKey = getCacheKey(request);
        Object result = redissonClient.getBucket(cacheKey).get();
        return Integer.valueOf(result.toString());
    }

    @Override
public ProductInventoryResponse decrease(InventoryRequest request) {
    ProductInventoryResponse productInventoryResponse = new ProductInventoryResponse();



    String cacheKey = getCacheKey(request); // 用于存储库存的键
    String cacheStreamKey = getCacheStreamKey(request); // 用于存储操作日志的键
    String lockKey = "LOCK_" + cacheKey; // 分布式锁的键

    RLock lock = redissonClient.getLock(lockKey);

    try {
        if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
            try {
                // 获取当前库存
                String currentStockStr = redissonClient.getBucket(cacheKey).get().toString();
                if (currentStockStr == null) {
                    throw new IllegalArgumentException(ERROR_CODE_KEY_NOT_FOUND);
                }

                int currentStock = Integer.parseInt(currentStockStr);
                int decreaseAmount = request.getInventory();

                // 检查库存是否充足
                if (currentStock < decreaseAmount) {
                    throw new IllegalArgumentException(ERROR_CODE_INVENTORY_NOT_ENOUGH);
                }

                // 更新库存
                int newStock = currentStock - decreaseAmount;
                redissonClient.getBucket(cacheKey).set(newStock);

                // 记录操作日志
                long currentTimeMillis = System.currentTimeMillis();
                Map<String, Object> logEntry = new HashMap<>();
                logEntry.put("action", "decrease");
                logEntry.put("from", currentStock);
                logEntry.put("to", newStock);
                logEntry.put("change", decreaseAmount);
                logEntry.put("by", request.getIdentifier());
                logEntry.put("timestamp", currentTimeMillis);

                redissonClient.getMap(cacheStreamKey).put("DECREASE_" + request.getIdentifier(), logEntry);

                // 构造成功响应
                productInventoryResponse.setSuccess(true);
                productInventoryResponse.setProductId(Long.valueOf(request.getProductId()));
                productInventoryResponse.setIdentifier(request.getIdentifier());
                return productInventoryResponse;
            } finally {
                lock.unlock(); // 确保锁被释放
            }
        } else {
            throw new IllegalStateException("Unable to acquire lock");
        }
    } catch (Exception e) {
        // 处理异常
        productInventoryResponse.setSuccess(false);
        productInventoryResponse.setProductId(Long.valueOf(request.getProductId()));
        productInventoryResponse.setIdentifier(request.getIdentifier());
        log.error("Error occurred while processing inventory decrease request: ", e);

        if (ERROR_CODE_KEY_NOT_FOUND.equals(e.getMessage())) {
            productInventoryResponse.setResponseCode(ERROR_CODE_KEY_NOT_FOUND);
        } else if (ERROR_CODE_OPERATION_ALREADY_EXECUTED.equals(e.getMessage())) {
            productInventoryResponse.setResponseCode(ERROR_CODE_OPERATION_ALREADY_EXECUTED);
            productInventoryResponse.setSuccess(true);
        } else if (ERROR_CODE_INVENTORY_NOT_ENOUGH.equals(e.getMessage())) {
            productInventoryResponse.setResponseCode(ERROR_CODE_INVENTORY_NOT_ENOUGH);
        } else {
            productInventoryResponse.setResponseCode(BIZ_ERROR.name());
        }

        productInventoryResponse.setResponseMessage(e.getMessage());
        return productInventoryResponse;
    }
}


    @Override
    public String getInventoryDecreaseLog(InventoryRequest request) {
        String luaScript =
                "local jsonString = redis.call('hget', KEYS[1], ARGV[1])\n" +
                        "return jsonString";

        String stream = redissonClient.getScript().eval(RScript.Mode.READ_WRITE,
                luaScript,
                RScript.ReturnType.STATUS,
                Arrays.asList(getCacheStreamKey(request)), "DECREASE_" + request.getIdentifier());
        return stream;
    }

    @Override
public ProductInventoryResponse increase(InventoryRequest request) {
    ProductInventoryResponse productInventoryResponse = new ProductInventoryResponse();

    String cacheKey = getCacheKey(request); // 库存键
    String cacheStreamKey = getCacheStreamKey(request); // 操作日志键
    String lockKey = "LOCK_" + cacheKey; // 分布式锁键

    RLock lock = redissonClient.getLock(lockKey);

    try {
        // 尝试获取锁（超时时间为10秒，锁自动释放时间为30秒）
        if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
            try {
                // 获取当前库存
                String currentStockStr = redissonClient.getBucket(cacheKey).get().toString();
                if (currentStockStr == null) {
                    throw new IllegalArgumentException(ERROR_CODE_KEY_NOT_FOUND);
                }

                int currentStock = Integer.parseInt(currentStockStr);
                int increaseAmount = request.getInventory();

                // 检查库存是否合理（可根据业务逻辑增加其他检查）
                if (increaseAmount < 0) {
                    throw new IllegalArgumentException("Invalid inventory increase amount");
                }

                // 更新库存
                int newStock = currentStock + increaseAmount;
                redissonClient.getBucket(cacheKey).set(newStock);

                // 检查操作是否已执行（去重逻辑）
                String operationKey = "INCREASE_" + request.getIdentifier();
                RMap<String, String> logMap = redissonClient.getMap(cacheStreamKey);
                if (logMap.containsKey(operationKey)) {
                    throw new IllegalArgumentException(ERROR_CODE_OPERATION_ALREADY_EXECUTED);
                }

                // 记录操作日志
                long currentTimeMillis = System.currentTimeMillis();
                Map<String, Object> logEntry = new HashMap<>();
                logEntry.put("action", "increase");
                logEntry.put("from", currentStock);
                logEntry.put("to", newStock);
                logEntry.put("change", increaseAmount);
                logEntry.put("by", request.getIdentifier());
                logEntry.put("timestamp", currentTimeMillis);

                logMap.put(operationKey, logEntry.toString());

                // 构造响应
                productInventoryResponse.setSuccess(true);
                productInventoryResponse.setProductId(Long.valueOf(request.getProductId()));
                productInventoryResponse.setIdentifier(request.getIdentifier());
                productInventoryResponse.setInventory(newStock);
                return productInventoryResponse;
            } finally {
                lock.unlock(); // 确保锁被释放
            }
        } else {
            throw new IllegalStateException("Unable to acquire lock");
        }
    } catch (Exception e) {
        // 处理异常
        productInventoryResponse.setSuccess(false);
        productInventoryResponse.setProductId(Long.valueOf(request.getProductId()));
        productInventoryResponse.setIdentifier(request.getIdentifier());
        log.error("Error occurred while processing inventory increase request: ", e);

        if (ERROR_CODE_KEY_NOT_FOUND.equals(e.getMessage())) {
            productInventoryResponse.setResponseCode(ERROR_CODE_KEY_NOT_FOUND);
        } else if (ERROR_CODE_OPERATION_ALREADY_EXECUTED.equals(e.getMessage())) {
            productInventoryResponse.setResponseCode(ERROR_CODE_OPERATION_ALREADY_EXECUTED);
            productInventoryResponse.setSuccess(true); // 如果是重复操作，仍然认为成功
        } else {
            productInventoryResponse.setResponseCode(BIZ_ERROR.name());
        }

        productInventoryResponse.setResponseMessage(e.getMessage());
        return productInventoryResponse;
    }
}


    @Override
    public void invalid(InventoryRequest request) {
        if (redissonClient.getBucket(getCacheKey(request)).isExists()) {
            redissonClient.getBucket(getCacheKey(request)).delete();
        }

        if (redissonClient.getBucket(getCacheStreamKey(request)).isExists()) {
            // 让流水记录的过期时间设置为24小时后，这样可以避免流水记录立即过期，对账出现问题
            redissonClient.getBucket(getCacheStreamKey(request)).expire(Instant.now().plus(24, ChronoUnit.HOURS));
        }
    }

    @NotNull
    private static String getCacheKey(InventoryRequest request) {
        return PRODUCT_KEY + request.getProductId();
    }

    @NotNull
    private static String getCacheStreamKey(InventoryRequest request) {
        return INVENTORY_STREAM_KEY + request.getProductId();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Integer count = 296;
        redissonClient.getBucket("pd:inventory:"+13).set(count);
    }
}




